/*
 * Project: Made 2 Order Software service package
 * Package: com.madetoordersoftware.service
 * File:    AbstractDao
 * Created: May 24, 2010
 */
/**
 * Copyright (c) 2010 Made 2 Order Software, LLC
 *
 * All rights reserved. No part of this software may be reproduced, stored in a retrieval system, or
 * transmitted in any
 * form or by any means, without the prior written permission of Made 2 Order Software.
 *
 * You may not distribute the URL or redistribute the files to third parties. You may transfer the files onto
 * a single
 * storage device so long as you can prove ownership of the software. You may not reverse engineer, decompile,
 * or
 * disassemble the software or media files. You may not modify or translate the software or media, or
 * distribute copies
 * of the software or media without the written consent of Made 2 Order Software, LLC.
 */
package com.made2ordersoftware.service;

import com.made2ordersoftware.exceptions.DAOException;
import java.io.Serializable;
import java.util.List;
import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * An abstract service layer class to help managed all data access objects.
 *
 * @param <T>  Concrete Data Access Object type to be used.
 * @param <ID>
 *
 * @author David Canning <dcannin@gmail.com>
 * @version 1.1
 */
@Transactional(Transactional.TxType.MANDATORY)
public abstract class AbstractDao<T, ID extends Serializable> implements IAbstractDao<T, ID>, Serializable {

    private static final Logger LOG = LoggerFactory.getLogger(AbstractDao.class);
    private static final long serialVersionUID = 1L;
    private final Class<T> entityClass;

    /**
     * Creates an instance of <code>AbstractDao</code> and sets the persistent unit for this application.
     *
     * @param entityClass The entity class being persisted.
     */
    public AbstractDao(Class<T> entityClass) {
        LOG.info("Creating AbstractDao with {}", entityClass);
        this.entityClass = entityClass;
    }

    /**
     * Creates/Adds the entity object to the database.
     *
     * @param entity Object to be created/added to the data source.
     *
     * @throws EntityExistsException Thrown if the entity already exist in system.
     * @throws DAOException          Any other database exception caught during the transaction execution.
     */
    @Override
    @Transactional(Transactional.TxType.MANDATORY)
    public void create(Object entity) throws EntityExistsException, DAOException {
        LOG.info("Creating entity {}", entity);
        getEntityManager().persist(entity);
        LOG.info("Entity created successfully");
    }

    @Override
    @Transactional(Transactional.TxType.MANDATORY)
    public void update(Object entity) throws DAOException {
        LOG.info("Updating entity {}", entity);
        getEntityManager().merge(entity);
        LOG.info("Entity updated successfully");
    }

    @Override
    @Transactional(Transactional.TxType.MANDATORY)
    public void delete(Object entity) throws DAOException {
        LOG.info("Deleting entity {}", entity);
        getEntityManager().remove(getEntityManager().merge(entity));
        LOG.info("Entity deleted successfully");
    }

    @Override
    public T findEntity(Object id) throws DAOException {
        return getEntityManager().find(entityClass, id);
    }

    @Override
    public List<T> findEntities() throws DAOException {
        LOG.info("Find all entities");
        return findEntities(true, -1, -1);
    }

    @Override
    public List<T> findEntities(int maxResults, int firstResult) throws DAOException {
        LOG.info("Find entities from {} to {}", firstResult, (firstResult + maxResults));
        return findEntities(false, maxResults, firstResult);
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<T> findEntities(boolean all, int maxResults, int firstResult) throws DAOException {
        CriteriaQuery<Object> cq = getEntityManager().getCriteriaBuilder().createQuery();
        cq.select(cq.from(entityClass));
        Query q = getEntityManager().createQuery(cq);
        if (!all) {
            q.setMaxResults(maxResults);
            q.setFirstResult(firstResult);
        }
        return q.getResultList();
    }

    @Override
    public int getEntityCount() throws DAOException {
        LOG.info("Finding count all entities");
        CriteriaQuery<Object> cq = getEntityManager().getCriteriaBuilder().createQuery();
        Root<T> rt = cq.from(entityClass);
        cq.select(getEntityManager().getCriteriaBuilder().count(rt));
        Query q = getEntityManager().createQuery(cq);
        return ((Number) q.getSingleResult()).intValue();
    }

    /**
     *
     * @return EntityManager objects
     */
    protected abstract EntityManager getEntityManager();

}
